package com.ah.bengbu.project;

import com.google.common.base.CaseFormat;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.ah.bengbu.project.RiverchiefConstant.*;

/**
 * 代码生成器，根据数据表名称生成对应的Model、Mapper、Service、Controller简化开发。
 */
public class CodeGenerator {
    //JDBC配置，请修改为你项目的实际配置
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";
    private static final String JDBC_USERNAME = "root";
    private static final String JDBC_PASSWORD = "123456";
    private static final String JDBC_DIVER_CLASS_NAME = "com.mysql.jdbc.Driver";

    private static final String PROJECT_PATH = System.getProperty("user.dir");//项目在硬盘上的基础路径
    private static final String TEMPLATE_FILE_PATH = PROJECT_PATH + "/src/java8/riverchief/src/test/resources/gengerator/templates";//模板位置

    private static final String JAVA_PATH = "/src/java8/riverchief/src/main/java"; //java文件路径
    private static final String RESOURCES_PATH = "/src/java8/riverchief/src/main/resources";//资源文件路径


    //增删改查的service类

    private static final String PACKAGE_PATH_SERVICE = packageConvertPath(SERVICE_PACKAGE);//生成的Service存放路径
    private static final String PACKAGE_PATH_DTO = packageConvertPath(DTO_PACKAGE);//生成的dto存放路径
    private static final String PACKAGE_PATH_CONTROLLER = packageConvertPath(CONTROLLER_PACKAGE);//生成的Controller存放路径
    private static final String PACKAGE_PATH_DOMAIN = packageConvertPath(MODEL_PACKAGE);//生成的domain存放路径
    private static final String PACKAGE_PATH_INFRASTRUCTURE = packageConvertPath(MAPPER_PACKAGE);//生成的infrastructure存放路径
    private static final String PACKAGE_PATH_HBM_XML = packageConvertPath(HBM_XML_PACKAGE);//生成的hbm.xml存放路径

    private static final String AUTHOR = "cjj";//@author
    private static final String DATE = new SimpleDateFormat("yyyy-MM-dd").format(new Date());//@date

    private static final String MODELNAME = "预警信息";
    private static final String TABLE = "t_hydrology_monitoring";
    private static final String TABLE_NAME = "EarlyWarning";
    private static final String BASE_MAPPING = "base-info";

    public static void main(String[] args) {
        //genCode("t_sys_user");
        //genCodeByCustomModelName(TABLE,TABLE_NAME);
        //用hibernate直接构造表实体生成表
        //genInfrastructure(TABLE, TABLE_NAME);
        //genDomain(TABLE, TABLE_NAME);
        //genDTO(TABLE, TABLE_NAME);
        //genService(TABLE, TABLE_NAME);
        //genController(TABLE, TABLE_NAME);
        genDoc(TABLE, TABLE_NAME);

    }

    private static void genDoc(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);

            data.put("baseRequestMapping", BASE_MAPPING);
            String modelRequestMapping = toUnderScoreCase(modelNameUpperCamel);
            data.put("modelRequestMapping", modelRequestMapping);

            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelName", MODELNAME);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + RESOURCES_PATH + PACKAGE_PATH_HBM_XML + "/"
                    + MODELNAME + "接口文档.md");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("interface-doc.ftl").process(data, new FileWriter(file));

            System.out.println(modelNameUpperCamel + "md接口文档生成成功");
        } catch (Exception e) {
            throw new RuntimeException("md接口文档生成失败", e);
        }


    }

    public static void genInfrastructure(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", DATE);
            data.put("author", AUTHOR);
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);

            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelName", MODELNAME);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_INFRASTRUCTURE + modelNameLowerCamel + "/"
                    + modelNameUpperCamel + "Repository.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("repository-infrastructure.ftl").process(data, new FileWriter(file));

            //将首字母小写
            String s = modelNameUpperCamel.substring(0, 1).toLowerCase() + modelNameUpperCamel.substring(1);
            //生成cfg文件
            File file1 = new File(PROJECT_PATH + RESOURCES_PATH + PACKAGE_PATH_HBM_XML + "/"
                    + s + ".hbm.xml");
            if (!file1.getParentFile().exists()) {
                file1.getParentFile().mkdirs();
            }
            cfg.getTemplate("hbm-xml.ftl").process(data, new FileWriter(file1));


            System.out.println(modelNameUpperCamel + "仓储impl生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成仓储impl失败", e);
        }

    }

    public static void genDomain(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", DATE);
            data.put("author", AUTHOR);
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);

            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelName", MODELNAME);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DOMAIN + modelNameLowerCamel + "/"
                    + modelNameUpperCamel + ".java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("entity-domain.ftl").process(data, new FileWriter(file));

            File file1 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DOMAIN + modelNameLowerCamel + "/"
                    + "I" + modelNameUpperCamel + ".java");
            if (!file1.getParentFile().exists()) {
                file1.getParentFile().mkdirs();
            }
            cfg.getTemplate("i-entity-domain.ftl").process(data, new FileWriter(file1));

            File file2 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DOMAIN + modelNameLowerCamel + "/"
                    + "I" + modelNameUpperCamel + "Repository.java");
            if (!file2.getParentFile().exists()) {
                file2.getParentFile().mkdirs();
            }
            cfg.getTemplate("i-repository-domain.ftl").process(data, new FileWriter(file2));

            File file3 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DOMAIN + modelNameLowerCamel + "/"
                    + modelNameUpperCamel + "Factory.java");
            if (!file3.getParentFile().exists()) {
                file3.getParentFile().mkdirs();
            }
            cfg.getTemplate("factory-domain.ftl").process(data, new FileWriter(file3));

            System.out.println(modelNameUpperCamel + "domain生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成domain失败", e);
        }


    }

    public static void genDTO(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", DATE);
            data.put("author", AUTHOR);
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);

            data.put("modelName", MODELNAME);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DTO + modelNameLowerCamel + "/"
                    + "CreateDTO.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("create-dto.ftl").process(data, new FileWriter(file));

            File file1 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DTO + modelNameLowerCamel + "/"
                    + "UpdateDTO.java");
            if (!file1.getParentFile().exists()) {
                file1.getParentFile().mkdirs();
            }
            cfg.getTemplate("update-dto.ftl").process(data, new FileWriter(file1));

            File file2 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_DTO + modelNameLowerCamel + "/"
                    + "GetResultDTO.java");
            if (!file2.getParentFile().exists()) {
                file2.getParentFile().mkdirs();
            }
            cfg.getTemplate("getresult-dto.ftl").process(data, new FileWriter(file2));

            System.out.println(modelNameUpperCamel + "的基本dto生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成dto失败", e);
        }

    }


    public static void genService(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", DATE);
            data.put("author", AUTHOR);
            data.put("modelName", MODELNAME);
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            data.put("modelNameUpperCamel", modelNameUpperCamel);
            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameLowerCamel + "/" +
                    "CreateService.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("create-service.ftl").process(data,
                    new FileWriter(file));

            File file1 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameLowerCamel + "/" +
                    "UpdateService.java");
            if (!file1.getParentFile().exists()) {
                file1.getParentFile().mkdirs();
            }
            cfg.getTemplate("update-service.ftl").process(data,
                    new FileWriter(file1));

            File file2 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameLowerCamel + "/" +
                    "GetService.java");
            if (!file2.getParentFile().exists()) {
                file2.getParentFile().mkdirs();
            }
            cfg.getTemplate("get-service.ftl").process(data,
                    new FileWriter(file2));

            File file3 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameLowerCamel + "/" +
                    "DeleteService.java");
            if (!file3.getParentFile().exists()) {
                file3.getParentFile().mkdirs();
            }
            cfg.getTemplate("delete-service.ftl").process(data,
                    new FileWriter(file3));

            File file4 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameLowerCamel + "/" +
                    "ListService.java");
            if (!file4.getParentFile().exists()) {
                file4.getParentFile().mkdirs();
            }
            cfg.getTemplate("list-service.ftl").process(data,
                    new FileWriter(file4));

            System.out.println(modelNameUpperCamel + "Service.java 生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成Service失败", e);
        }
    }

    public static void genController(String tableName, String modelName) {
        try {
            freemarker.template.Configuration cfg = getConfiguration();

            Map<String, Object> data = new HashMap<>();
            data.put("date", DATE);
            data.put("author", AUTHOR);
            String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
            data.put("baseRequestMapping", BASE_MAPPING);

            String modelNameLowerCamel = tableNameConvertLowerCamel(modelNameUpperCamel);

            String modelRequestMapping = toUnderScoreCase(modelNameUpperCamel);
            data.put("modelRequestMapping", modelRequestMapping);

            data.put("modelName", MODELNAME);
            data.put("modelNameUpperCamel", modelNameUpperCamel);
            data.put("modelNameLowerCamel", modelNameLowerCamel);
            data.put("basePackage", BASE_PACKAGE);

            File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_CONTROLLER + modelNameLowerCamel + "/"
                    + modelNameUpperCamel + "Controller.java");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("controller-restful.ftl").process(data, new FileWriter(file));
            //cfg.getTemplate("controller.ftl").process(data, new FileWriter(file));

            System.out.println(modelNameUpperCamel + "Controller.java 生成成功");
        } catch (Exception e) {
            throw new RuntimeException("生成Controller失败", e);
        }

    }

    private static freemarker.template.Configuration getConfiguration() throws IOException {
        freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23);
        cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_FILE_PATH));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        return cfg;
    }

    private static String tableNameConvertLowerCamel(String tableName) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, tableName.toLowerCase());
    }

    /**
     * 首字母大写 其余字母小写
     *
     * @param tableName
     * @return
     */
    private static String tableNameConvertUpperCamel(String tableName) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName.toLowerCase());

    }

    private static String tableNameConvertMappingPath(String tableName) {
        tableName = tableName.toLowerCase();//兼容使用大写的表名
        return "/" + (tableName.contains("_") ? tableName.replaceAll("_", "/") : tableName);
    }

    private static String modelNameConvertMappingPath(String modelName) {
        String tableName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, modelName);
        return tableNameConvertMappingPath(tableName);
    }

    private static String packageConvertPath(String packageName) {
        return String.format("/%s/", packageName.contains(".") ? packageName.replaceAll("\\.", "/") : packageName);
    }

    /**
     * 驼峰命名转换
     * @return
     * 		toUnderScoreCase("helloWorld") = "hello-world"
     */
    private static String toUnderScoreCase(String s) {
        if (s == null) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            boolean nextUpperCase = true;

            if (i < (s.length() - 1)) {
                nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
            }

            if ((i > 0) && Character.isUpperCase(c)) {
                if (!upperCase || !nextUpperCase) {
                    sb.append("-");
                }
                upperCase = true;
            } else {
                upperCase = false;
            }

            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

}
